﻿// <copyright file="PlayerChess.cs" company="ALCPU">
// Copyright (c) 2010 All Right Reserved
// </copyright>
// <author>Arthur Liberman</author>
// <email>Arthur_Liberman@hotmail.com</email>
// <date>03-30-2010</date>
// <summary>Contains WPF Player definition.</summary>

namespace Chess
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using Chess.Figures;
    using ChessDefinitions;

    /// <summary>
    /// WPF Player class
    /// </summary>
    public class PlayerChess : PlayerBase
    {
        /// <summary>
        /// The gameboard.
        /// </summary>
        private Gameboard gameboard;

        /// <summary>
        /// The currently selected piece.
        /// </summary>
        private IPiece selectedPiece;

        /// <summary>
        /// Initializes a new instance of the <see cref="PlayerChess"/> class.
        /// </summary>
        /// <param name="color">The piece color.</param>
        /// <param name="gameboard">The gameboard.</param>
        public PlayerChess(IPieceColor color, Gameboard gameboard) : base(color)
        {
            IPiece newPiece;
            int offset = 0;

            this.gameboard = gameboard;
            this.selectedPiece = null;

            if (color == IPieceColor.White)
            {
                offset = 7;
            }
            else if (color == IPieceColor.Black)
            {
                offset = 0;
            }

            for (int i = 0; i < 8; i++)
            {
                newPiece = new Pawn(color, gameboard, new Position(i, Math.Abs(offset - 1)));
                newPiece.GameEvent += new PieceEvent(this.Piece_GameEvent);
                this.PieceList.Add(newPiece);
            }

            newPiece = new Rook(color, gameboard, new Position(0, offset));
            newPiece.GameEvent += new PieceEvent(this.Piece_GameEvent);
            this.PieceList.Add(newPiece);
            newPiece = new Rook(color, gameboard, new Position(7, offset));
            newPiece.GameEvent += new PieceEvent(this.Piece_GameEvent);
            this.PieceList.Add(newPiece);
            newPiece = new Knight(color, gameboard, new Position(1, offset));
            newPiece.GameEvent += new PieceEvent(this.Piece_GameEvent);
            this.PieceList.Add(newPiece);
            newPiece = new Knight(color, gameboard, new Position(6, offset));
            newPiece.GameEvent += new PieceEvent(this.Piece_GameEvent);
            this.PieceList.Add(newPiece);
            newPiece = new Bishop(color, gameboard, new Position(2, offset));
            newPiece.GameEvent += new PieceEvent(this.Piece_GameEvent);
            this.PieceList.Add(newPiece);
            newPiece = new Bishop(color, gameboard, new Position(5, offset));
            newPiece.GameEvent += new PieceEvent(this.Piece_GameEvent);
            this.PieceList.Add(newPiece);
            newPiece = new Queen(color, gameboard, new Position(3, offset));
            newPiece.GameEvent += new PieceEvent(this.Piece_GameEvent);
            this.PieceList.Add(newPiece);
            newPiece = new King(color, gameboard, new Position(4, offset));
            newPiece.GameEvent += new PieceEvent(this.Piece_GameEvent);
            this.PieceList.Add(newPiece);
            this.King = newPiece;
        }

        /// <summary>
        /// Prevents a default instance of the <see cref="PlayerChess"/> class from being created.
        /// </summary>
        private PlayerChess() : base(0)
        {
        }

        /// <summary>
        /// Gets or sets the king.
        /// </summary>
        /// <value>A reference to the user's King.</value>
        public IPiece King { get; protected set; }

        /// <summary>
        /// Starts the turn.
        /// </summary>
        public override void StartTurn()
        {
            this.selectedPiece = null;
            this.IsTurnActive = true;
        }

        /// <summary>
        /// Ends the current turn.
        /// </summary>
        public override void EndTurn()
        {
            this.IsTurnActive = false;
            this.selectedPiece = null;
            this.gameboard.RecolorGameboard();
            this.NotifyOfGameEvent(this, GameEventType.TurnEnded);
        }

        /// <summary>
        /// Handles the user input.
        /// </summary>
        /// <param name="eventArgs">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        public override void HandleUserInput(EventArgs eventArgs)
        {
            if (eventArgs == null || !(eventArgs is MouseButtonEventArgs))
            {
                throw new Exception("User input handling error, eventArgs is null or no MouseButtonEventArgs");
            }
            else if (!this.IsTurnActive)
            {
                return;
            }

            MouseButtonEventArgs mouseArgs = eventArgs as MouseButtonEventArgs;
            Position position = this.gameboard.GetMousePosition(mouseArgs);
            IPiece piece = this.gameboard.GetFigureByPosition(position);

            if (mouseArgs.RightButton == MouseButtonState.Pressed)
            {
                this.selectedPiece = null;
                this.gameboard.RecolorGameboard();
                return;
            }
            else if (piece != null && piece == this.selectedPiece)
            {
                return;
            }

            if (piece != null && piece.Color == this.Color &&
                (this.selectedPiece == null || this.selectedPiece != piece))
            {
                this.gameboard.RecolorGameboard();
                this.selectedPiece = piece;
                this.gameboard.ShowSelected(piece.Position);
                this.gameboard.ShowAvailable(piece);
                this.gameboard.ShowAttackable(piece);
            }
            else if (piece == null && this.selectedPiece != null &&
                this.selectedPiece.MoveOptions.Contains(position))
            {
                this.selectedPiece.MoveTo(position);
                this.NotifyOfGameEvent(this, GameEventType.Move);
                this.EndTurn();
            }
            else if (piece != null && this.selectedPiece != null && this.selectedPiece.AttackOptions.Contains(position))
            {
                if (this.selectedPiece.Attack(position))
                {
                    this.NotifyOfGameEvent(this, GameEventType.Attack);
                }

                this.EndTurn();
            }
        }

        /// <summary>
        /// Checks the endgame.
        /// </summary>
        /// <param name="oppositePlayer">The opposite player.</param>
        /// <returns>
        ///     <c>GameEventType.None</c> if there were no threats from either side.
        ///     <c>GameEventType.Check</c> if there was a check from the opposing side.
        ///     <c>GameEventType.Checkmate</c> if there was a checkmate from the opposing side.
        ///     <c>GameEventType.Stalemate</c> if there is a stalemate.
        /// </returns>
        public override GameEventType CheckEndgame(PlayerBase oppositePlayer)
        {
            List<Position> oppositeKingMoves;
            List<Position> attacks;
            GameEventType endgame = GameEventType.None;

            if (oppositePlayer == null || !(oppositePlayer is PlayerChess))
            {
                throw new Exception("oppositePlayer is not of PlayerChess type");
            }

            oppositeKingMoves = ((PlayerChess)oppositePlayer).King.AllMoveOptions;
            foreach (IPiece piece in this.PieceList)
            {
                attacks = piece.AttackOptions;
                foreach (Position target in attacks)
                {
                    if (this.gameboard.Figures[target] != null && this.gameboard.Figures[target] is King)
                    {
                        endgame = GameEventType.Check;
                        break;
                    }
                }
            }

            // If there is no Check and no Checkmate, check for Stalemate.
            List<Position> posList = new List<Position>();
            bool isStalemate = false;

            // Aggregate all the possible moves from this player's pieces
            foreach (IPiece piece in this.PieceList)
            {
                posList.AddRange(piece.AllMoveOptions.ToList());
                posList.Add(piece.Position);
            }

            // Check each possible move of the opposite king's moves
            foreach (Position position in oppositeKingMoves)
            {
                isStalemate = true;
                if (!posList.Contains(position))
                {
                    isStalemate = false;
                    break;
                }
            }

            if (isStalemate)
            {
                if (endgame == GameEventType.Check)
                {
                    return GameEventType.Checkmate;
                }

                // else
                // {
                //    return GameEventType.Stalemate;
                // }
            }

            return endgame;
        }

        /// <summary>
        /// Piece's game event handler.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="gea">The <see cref="ChessDefinitions.GameEventArgs"/> instance containing the event data.</param>
        protected void Piece_GameEvent(IPiece sender, GameEventArgs<IPiece> gea)
        {
            if (gea != null && sender != null)
            {
                switch (gea.GameEventType)
                {
                    case GameEventType.None:
                        break;
                    case GameEventType.Attack:
                        if (sender is King)
                        {
                            this.NotifyOfGameEvent(this, GameEventType.Checkmate);
                        }

                        this.PieceList.Remove(sender);
                        this.gameboard.Figures[sender.Position] = null;
                        break;
                    case GameEventType.Crowned:
                        IPiece piece = new Checkers.King(this.Color, this.gameboard, sender.Position);
                        this.PieceList.Remove(sender);
                        this.PieceList.Add(piece);
                        break;
                }
            }
        }
    }
}
